From 544a856af58d05d324b3056daa5ac7018a99d43e Mon Sep 17 00:00:00 2001 From: "Ian.Campbell@xensource.com" Date: Wed, 11 Jan 2006 15:53:59 +0000 Subject: [PATCH] Pass NMIs to DOM0 via a dedicated callback, Xen/Linux x86_64 support. Register our NMI handler with Xen. Do an iret via the hypervisor whenever the return CS is > RING0. Add include/asm-xen/asm-x86_64/nmi.h as a Xen modified copy of include/asm-x86_64/nmi.h in order that we can implement get_nmi_reason() in a manner suitable for Xen. Signed-off-by: Ian Campbell --- .../arch/xen/x86_64/kernel/entry.S | 24 ++++-- .../arch/xen/x86_64/kernel/setup.c | 1 + .../arch/xen/x86_64/kernel/traps.c | 4 + .../include/asm-xen/asm-x86_64/hypercall.h | 12 ++- .../asm-x86_64/mach-xen/setup_arch_post.h | 3 + .../include/asm-xen/asm-x86_64/nmi.h | 75 +++++++++++++++++++ 6 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/entry.S b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/entry.S index c8fa7871d8..9ecc36e5ca 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/entry.S +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/entry.S @@ -121,19 +121,19 @@ .endm /* - * Must be consistent with the definition in arch_x86_64.h: - * struct switch_to_user { + * Must be consistent with the definition in arch-x86_64.h: + * struct iret_context { * u64 rax, r11, rcx, flags, rip, cs, rflags, rsp, ss; * }; * #define VGCF_IN_SYSCALL (1<<8) */ - .macro SWITCH_TO_USER flag + .macro HYPERVISOR_IRET flag subq $8*4,%rsp # reuse rip, cs, rflags, rsp, ss in the stack movq %rax,(%rsp) movq %r11,1*8(%rsp) movq %rcx,2*8(%rsp) # we saved %rcx upon exceptions movq $\flag,3*8(%rsp) - movq $__HYPERVISOR_switch_to_user,%rax + movq $__HYPERVISOR_iret,%rax syscall .endm @@ -225,7 +225,7 @@ sysret_check: jnz sysret_careful XEN_UNBLOCK_EVENTS(%rsi) RESTORE_ARGS 0,8,0 - SWITCH_TO_USER VGCF_IN_SYSCALL + HYPERVISOR_IRET VGCF_IN_SYSCALL /* Handle reschedules */ /* edx: work, edi: workmask */ @@ -478,7 +478,7 @@ kernel_mode: orb $3,1*8(%rsp) iretq user_mode: - SWITCH_TO_USER 0 + HYPERVISOR_IRET 0 /* edi: workmask, edx: work */ retint_careful: @@ -719,6 +719,16 @@ ENTRY(do_hypervisor_callback) # do_hyperviosr_callback(struct *pt_regs) call evtchn_do_upcall jmp error_exit +ENTRY(nmi) + zeroentry do_nmi_callback +ENTRY(do_nmi_callback) + addq $8, %rsp + call do_nmi + RESTORE_REST + XEN_BLOCK_EVENTS(%rsi) + GET_THREAD_INFO(%rcx) + jmp retint_restore_args + ALIGN restore_all_enable_events: XEN_UNBLOCK_EVENTS(%rsi) # %rsi is already set up... @@ -733,7 +743,7 @@ scrit: /**** START OF CRITICAL REGION ****/ orb $3,1*8(%rsp) iretq crit_user_mode: - SWITCH_TO_USER 0 + HYPERVISOR_IRET 0 14: XEN_LOCKED_BLOCK_EVENTS(%rsi) XEN_PUT_VCPU_INFO(%rsi) diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c index f82f8aac84..ef7deb8938 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c @@ -62,6 +62,7 @@ #include #include "setup_arch_pre.h" #include +#include #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) #define PFN_PHYS(x) ((x) << PAGE_SHIFT) #define end_pfn_map end_pfn diff --git a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c index 227042ba63..6229915b11 100644 --- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c +++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c @@ -559,9 +559,11 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs) printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); printk("You probably have a hardware problem with your RAM chips\n"); +#if 0 /* XEN */ /* Clear and disable the memory parity error line. */ reason = (reason & 0xf) | 4; outb(reason, 0x61); +#endif /* XEN */ } static void io_check_error(unsigned char reason, struct pt_regs * regs) @@ -569,12 +571,14 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs) printk("NMI: IOCK error (debug interrupt?)\n"); show_registers(regs); +#if 0 /* XEN */ /* Re-enable the IOCK line, wait for a few seconds */ reason = (reason & 0xf) | 8; outb(reason, 0x61); mdelay(2000); reason &= ~8; outb(reason, 0x61); +#endif /* XEN */ } static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h index bb338772d0..521f004c00 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h @@ -287,9 +287,9 @@ HYPERVISOR_vcpu_op( } static inline int -HYPERVISOR_switch_to_user(void) +HYPERVISOR_iret(void) { - return _hypercall0(int, switch_to_user); + return _hypercall0(int, iret); } static inline int @@ -307,6 +307,14 @@ HYPERVISOR_suspend( SHUTDOWN_suspend, srec); } +static inline int +HYPERVISOR_nmi_op( + unsigned long op, + unsigned long arg) +{ + return _hypercall2(int, nmi_op, op, arg); +} + #endif /* __HYPERCALL_H__ */ /* diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/mach-xen/setup_arch_post.h b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/mach-xen/setup_arch_post.h index 7be26e8660..bad118e44c 100644 --- a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/mach-xen/setup_arch_post.h +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/mach-xen/setup_arch_post.h @@ -35,6 +35,7 @@ void __init machine_specific_modify_cpu_capabilities(struct cpuinfo_x86 *c) extern void hypervisor_callback(void); extern void failsafe_callback(void); +extern void nmi(void); static void __init machine_specific_arch_setup(void) { @@ -43,5 +44,7 @@ static void __init machine_specific_arch_setup(void) (unsigned long) failsafe_callback, (unsigned long) system_call); + HYPERVISOR_nmi_op(XENNMI_register_callback, (unsigned long)&nmi); + machine_specific_modify_cpu_capabilities(&boot_cpu_data); } diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h new file mode 100644 index 0000000000..1c5d28dad2 --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h @@ -0,0 +1,75 @@ +/* + * linux/include/asm-i386/nmi.h + */ +#ifndef ASM_NMI_H +#define ASM_NMI_H + +#include + +#include + +struct pt_regs; + +typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu); + +/** + * set_nmi_callback + * + * Set a handler for an NMI. Only one handler may be + * set. Return 1 if the NMI was handled. + */ +void set_nmi_callback(nmi_callback_t callback); + +/** + * unset_nmi_callback + * + * Remove the handler previously set. + */ +void unset_nmi_callback(void); + +#ifdef CONFIG_PM + +/** Replace the PM callback routine for NMI. */ +struct pm_dev * set_nmi_pm_callback(pm_callback callback); + +/** Unset the PM callback routine back to the default. */ +void unset_nmi_pm_callback(struct pm_dev * dev); + +#else + +static inline struct pm_dev * set_nmi_pm_callback(pm_callback callback) +{ + return 0; +} + +static inline void unset_nmi_pm_callback(struct pm_dev * dev) +{ +} + +#endif /* CONFIG_PM */ + +extern void default_do_nmi(struct pt_regs *); +extern void die_nmi(char *str, struct pt_regs *regs); + +static inline unsigned char get_nmi_reason(void) +{ + shared_info_t *s = HYPERVISOR_shared_info; + unsigned char reason = 0; + + /* construct a value which looks like it came from + * port 0x61. + */ + if (test_bit(_XEN_NMIREASON_io_error, &s->arch.nmi_reason)) + reason |= 0x40; + if (test_bit(_XEN_NMIREASON_parity_error, &s->arch.nmi_reason)) + reason |= 0x80; + + return reason; +} + +extern int panic_on_timeout; +extern int unknown_nmi_panic; + +extern int check_nmi_watchdog(void); + +#endif /* ASM_NMI_H */ -- 2.30.2